﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Text;

using agree.configuration;
using agree.schema;
using agree.semi;
using agree.mrs;

using alib;
using alib.Collections;
using alib.Collections.ReadOnly;
using alib.Concurrency;
using alib.Debugging;
using alib.Enumerable;
using alib.String;
using alib.String.Builder;

namespace agree.Pmrs
{
#if !PMRS
	public class atomic_base
	{
	}

	public class atomic_selfhost : atomic_base
	{
		public atomic_selfhost(PortableMrs pmrs, String s_type)
		{
		}
	}
	public class atomic_type : atomic_selfhost
	{
		public atomic_type(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	}
	public class atomic_string : atomic_selfhost
	{
		public atomic_string(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	}
	public class atomic_hosted : atomic_base
	{
		public atomic_selfhost label_type;
	}
	public class atomic_bag<T> : atomic_hosted  /*, IList<T>*/ where T : complex
	{
		public void Add(T t)
		{
		}
	}
	public class complex : atomic_hosted
	{
		public complex(PortableMrs pmrs, atomic_selfhost label_type)
		{
		}

		public Dictionary<String, atomic_base> rgav;

		public void SetFeatureValue(String s_feat, atomic_base child)
		{
		}
	}
	public class variable : complex
	{
		public variable(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
			this.tag = pmrs.next_tag++;
		}
		public int tag;
		public String SkolemString { get { return GetType().Name.ToLower() + tag; } }
	}
	public class index : variable
	{
		public index(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	}
	public class X : index
	{
		public X(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	}
	public class U : index
	{
		public U(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	}
	public class E : index
	{
		public E(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	}
	public class H : variable
	{
		public H(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	}
	public class relation : complex
	{
		public relation(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
		}
	}
	public class qeq : complex
	{
		public qeq(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
		}
	}
	public class hook : complex
	{
		public hook(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
		}
	}

	public class PortableMrs : atomic_selfhost
	{
		public PortableMrs(IRtParent parent, MrsManager mrsm)
			: base(null, "")
		{
		}

		public PortableMrs(IRtParent parent, Tfs tfs)
			: this(parent, tfs.mrsm)
		{
		}
		public int next_tag = 1;

		public hook hook;

		public atomic_bag<relation> rels;

		public atomic_bag<qeq> hcons;

		public void EquateQeqScopes()
		{
			//foreach (var qeq in hcons)
			//	qeq.Harg.JoinWith(qeq.Larg);
		}

		public Tfs Write(TfsUnifier fsu, FeatMgr ftm, Dictionary<String, String> rel_name_map, Tfs.Flags tfs_flags)
		{
			return null;
		}
	}
	
#else

	public struct av_feat
	{
		public av_feat(String s_feat, atomic_base value)
		{
			this.s_feat = s_feat;
			this.value = value;
		}
		public av_feat(KeyValuePair<String, atomic_base> kvp)
			: this(kvp.Key, kvp.Value)
		{
		}
		public String s_feat;
		public atomic_base value;
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public abstract class atomic_base : identity_node, ILabelRenderNode, ICorefRenderNode, IFeatValueRenderNode
	{
		public atomic_base(IRtParent parent)
			: base(parent)
		{
			if ((this.pmrs = parent as PortableMrs) == null)
				this.pmrs = (PortableMrs)this;
			this.id = this.pmrs._next_id++;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly PortableMrs pmrs;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int id;

		public HashSet<complex> parents = new HashSet<complex>();

		public void AddParent(complex par)
		{
			if (!parents.Add(par))
			{
				//throw new Exception();
				// QEQs equated below 'handle' but not coreferenced above will cause this
				return;
			}
			if (parents.Count == 2)
			{
				if (coref_num != -1)
					throw new Exception();
				coref_num = pmrs.GetFreeCorefNum();
			}
		}
		public bool RemoveParent(complex par)
		{
			if (parents.Count == 0)
				throw new Exception();
			if (!parents.Remove(par))
				return false;
			if (parents.Count == 1)
			{
				if (coref_num == -1)
					throw new Exception();
				pmrs.FreeCorefNum(coref_num);
				coref_num = -1;
			}
			else if (parents.Count == 0 && this is variable)
			{
				pmrs.variables[((variable)this).tag - 1] = null;
			}
			return true;
		}

		public abstract atomic_selfhost HostType { get; }

		public int coref_num = -1;

		public int m_level;

		public int FindLevel()
		{
			if (parents.Count == 0)
				return 0;
			if (m_level == 0 && !(this is PortableMrs))
				m_level = parents.Max(p => p.FindLevel()) + 1;

			Debug.Print("{0}", m_level);

			return m_level;
		}

		public bool IsCoreferenced
		{
			get
			{
				if ((coref_num != -1) != (parents.Count > 1))
					throw new Exception();
				return coref_num != -1;
			}
		}

		public atomic_base MasterCoref
		{
			get
			{
				return IsCoreferenced ? parents.ExceptWhere(p => p is hook).ArgMin(a => a.id) : null;
			}
		}

		public override String ToString()
		{
			//return String.Format("#{0} {1}", id, t.Name);
			return String.Format("{0}", this.Label);
		}

		public abstract String Label { get; }

		public virtual String CorefTag
		{
			get { return coref_num == -1 ? null : coref_num.ToString(); }
		}

		public virtual IEnumerable<IRenderPairs> FeatValuePairs { get { return Collection<IRenderPairs>.None; } }

		public virtual IEnumerable<av_feat> fvp() { return Collection<av_feat>.None; }

		public IEnumerable<atomic_base> descendants(String s_feat)
		{
			return all_arcs(s_feat).Select(avf => avf.value).Distinct();
		}
		public IEnumerable<av_feat> all_arcs(String s_feat)
		{
			foreach (var avf in fvp())
			{
				foreach (var qq in avf.value.all_arcs(avf.s_feat))
					yield return qq;
			}
			yield return new av_feat(s_feat, this);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class atomic_hosted : atomic_base
	{
		public atomic_hosted(PortableMrs pmrs, atomic_selfhost label_type)
			: base(pmrs)
		{
			this.label_type = label_type;
		}

		public atomic_selfhost label_type;

		public override String Label { get { return label_type.Label; } }

		public override atomic_selfhost HostType { get { return label_type; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class atomic_bag<T> : atomic_hosted, IList<T> where T : complex
	{
		public atomic_bag(PortableMrs pmrs, atomic_selfhost label_type)
			: base(pmrs, label_type)
		{
			this.bag = new List<T>();
		}
		public override String Label { get { return "(bag)"; } }

		readonly List<T> bag;

		public T this[int index]
		{
			get { return bag[index]; }
			set { bag[index] = value; }
		}

		public bool Contains(T item) { return bag.Contains(item); }
		public int IndexOf(T item) { return bag.IndexOf(item); }
		public int Count { get { return bag.Count; } }
		public bool IsReadOnly { get { return false; } }
		public void RemoveAt(int index) { bag.RemoveAt(index); }
		public void Add(T item) { bag.Add(item); }
		public void Insert(int index, T item) { bag.Insert(index, item); }
		public void Clear() { bag.Clear(); }
		public bool Remove(T item) { return bag.Remove(item); }
		public IEnumerator<T> GetEnumerator() { return bag.GetEnumerator(); }
		IEnumerator IEnumerable.GetEnumerator() { return bag.GetEnumerator(); }
		public void CopyTo(T[] array, int arrayIndex) { bag.CopyTo(array, arrayIndex); }

		//public override IEnumerable<IRenderPairs> FeatValuePairs
		//{
		//	get
		//	{
		//		throw not.impl;
		//	}
		//}
		public override IEnumerable<av_feat> fvp()
		{
			for (int i = 0; i < bag.Count; i++)
				yield return new av_feat(i.ToString(), bag[i]);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public abstract class atomic_selfhost : atomic_base
	{
		public atomic_selfhost(IRtParent parent, String s_type)
			: base(parent)
		{
			this.s_type = s_type;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public String s_type;

		public override String Label { get { return s_type; } }

		public override atomic_selfhost HostType { get { return this; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class atomic_type : atomic_selfhost
	{
		public atomic_type(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class atomic_string : atomic_selfhost
	{
		public atomic_string(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}

		public override String Label { get { return s_type.DoubleQuote(); } }

		public String NoQuotes { get { return s_type; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class atomic_int : atomic_selfhost
	{
		public atomic_int(PortableMrs pmrs, int i)
			: base(pmrs, i.ToString())
		{
		}

		public override String Label { get { return s_type; } }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class complex : atomic_hosted, IFeatValueRenderNode
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Dictionary<String, atomic_base> rgav;

		public complex(PortableMrs pmrs, atomic_selfhost label_type)
			: base(pmrs, label_type)
		{
			this.rgav = new Dictionary<String, atomic_base>(StringComparer.OrdinalIgnoreCase);
		}

		public virtual void SetFeatureValue(String s_feat, atomic_base child)
		{
			atomic_base old_child;
			if (!rgav.TryGetValue(s_feat, out old_child))
				rgav.Add(s_feat, child);
			else
			{
				if (!old_child.RemoveParent(this))
					throw new Exception();
				rgav[s_feat] = child;
			}
			child.AddParent(this);
		}

		public override IEnumerable<av_feat> fvp()
		{
			return rgav.Select(kvp => new av_feat(kvp)).Where(a => a.value != null);
		}

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		public IRenderPairs[] _dbg { get { return FeatValuePairs.ToArray(); } }

		public override IEnumerable<IRenderPairs> FeatValuePairs
		{
			get
			{
				var mrsm = base.pmrs.MrsManager;

				return fvp()
					.OrderBy(a => mrsm.ftm.feat_map[a.s_feat].i_feat, mrsm._disp_order_cmp)
					.Select(avf =>
					{
						String s = avf.s_feat.ToUpper();
						return new FeatureValueNode(s, avf.value, true);
					});
			}
		}

		public void ChangeVariable(variable vold, variable vnew)
		{
			foreach (var kvp in rgav.ToArray())
				if (kvp.Value == vold)
					SetFeatureValue(kvp.Key, vnew);
		}

		public void DeleteFeature(String s_feat)
		{
			atomic_base cur;
			if (rgav.TryGetValue(s_feat, out cur))
			{
				cur.RemoveParent(this);
				rgav.Remove(s_feat);
			}
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class constant : atomic_type
	{
		public constant(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class variable : complex
	{
		public variable(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
			this.tag = pmrs.next_tag++;

			if (pmrs.variables.Count != tag - 1)
				throw new Exception();
			pmrs.variables.Add(this);
		}

		public int tag;

		public String SkolemString { get { return GetType().Name.ToLower() + tag; } }

		public abstract Edge.Flag SkolemValue { get; }

		public void JoinWith(variable other)
		{
			if (other.tag < this.tag)
				other.JoinWith(this);
			else if (this != other)
			{
				foreach (var par in other.parents.ToArray())
					par.ChangeVariable(other, this);
			}
		}

		public override String ToString()
		{
			return base.ToString() + " " + SkolemString;
		}

		public String StringTag { get { return SkolemString; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class index : variable
	{
		public index(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class E : index
	{
		public E(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
		public override Edge.Flag SkolemValue
		{
			get { return Edge.Flag.EtmSkolemValue | agree.Edge.Flag.MrsVarTypeE | (Edge.Flag)tag; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class X : index
	{
		public X(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}
		public override Edge.Flag SkolemValue
		{
			get { return Edge.Flag.EtmSkolemValue | agree.Edge.Flag.MrsVarTypeX | (Edge.Flag)tag; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class U : index
	{
		public U(PortableMrs pmrs, String s_stype)
			: base(pmrs, s_stype)
		{
		}
		public override Edge.Flag SkolemValue
		{
			get { return Edge.Flag.EtmSkolemValue | agree.Edge.Flag.MrsVarTypeU | (Edge.Flag)tag; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class H : variable
	{
		public H(PortableMrs pmrs, String s_type)
			: base(pmrs, s_type)
		{
		}

		public override Edge.Flag SkolemValue
		{
			get { return Edge.Flag.EtmSkolemValue | agree.Edge.Flag.MrsVarTypeH | (Edge.Flag)tag; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class relation : complex
	{
		public relation(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
		}

		public override void SetFeatureValue(String s_feat, atomic_base child)
		{
			if (s_feat.ToLower() == "pred")
			{
				if (child is variable)
					throw new Exception();
				base.label_type = child.HostType;
			}
			else
				base.SetFeatureValue(s_feat, child);
		}

		public atomic_selfhost Pred { get { return (atomic_selfhost)label_type; } }

		public String Text
		{
			get
			{
				var lbl = (H)rgav["lbl"];

				String s = String.Format("{0}: {1}({2})",
					lbl.SkolemString,
					label_type.s_type,
					fvp().ExceptWhere(z => z.s_feat == "lbl").Select(fp =>
					{
						var v = fp.value as variable;
						String st;
						if (v == null)
							st = fp.value.Label;
						else
						{
							st = v.SkolemString;

							//if (this == v.MasterCoref)
							st += pmrs.FormatEdge(v);
						}
						return st;
					}).StringJoin(", "));
				return s;
			}
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class qeq : complex
	{
		public qeq(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
		}

		public H Harg { get { return (H)rgav["harg"]; } }

		public H Larg { get { return (H)rgav["larg"]; } }

		public String Text { get { return String.Format("{0} qeq {1}", Harg.SkolemString, Larg.SkolemString); } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class hook : complex
	{
		public hook(PortableMrs pmrs, String s_type)
			: base(pmrs, new atomic_type(pmrs, s_type))
		{
		}

		public H Ltop { get { return (H)rgav["ltop"]; } }

		public index Index { get { return (index)rgav["index"]; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class PortableMrs : atomic_selfhost, ρρMrsManager, ρρDisplayName, ρρRelsCount
	{
		public PortableMrs(IRtParent parent, MrsManager mrsm)
			: base(parent, "mrs")
		{
			this.mrsm = mrsm;
			this.rels = new atomic_bag<relation>(this, this);
			this.hcons = new atomic_bag<qeq>(this, this);
			this.variables = new List<variable>();
		}

		public PortableMrs(IRtParent parent, Tfs tfs)
			: this(parent, tfs.mrsm)
		{
			this.s_disp_name = "Pmrs for " + tfs.ShortDisplayInfo();
			new _tfs_build_helper(this, tfs);
		}

		public Tfs Write(TfsUnifier fsu, Restrictor r, Dictionary<String, String> rel_name_map, Tfs.Flags tfs_flags)
		{
			List<arr_tfs_entry> late = new List<arr_tfs_entry>();

			var twh = new _tfs_walk_helper(this, r, ate => late.Add(ate), rel_name_map, tfs_flags.HasFlag(Tfs.Flags.Skolemized));

			Type t_mrs = fsu.td["mrs"];
			Edge e_mrs = fsu.tu.CreateEdge(t_mrs, 1, false);
			var tfs = new ArrayTfs(this, this, r, tfs_flags, e_mrs, late, -twh.next_coref_mark - 1);

			var atfs = Unification.Expand(r, tfs, tfs_flags);
			if (atfs == null)
				throw new Exception("The MRS is not well-formed in the target grammar.");

			atfs._set_trace(this);

			return atfs;
		}

		///////////////////////////////////////////////////////////////////////
		/// 
		String s_disp_name;
		public String DisplayName
		{
			get { return this.s_disp_name; }
			set { this.s_disp_name = value; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		/// 
		MrsManager mrsm;
		public MrsManager MrsManager
		{
			get { return this.mrsm; }
			set { this.mrsm = value; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////

		public int _next_id = 1;
		public int next_tag = 1;

		public hook hook;

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		public atomic_bag<relation> rels;

		public atomic_bag<qeq> hcons;

		public int RelsCount
		{
			get { return rels.Count; }
			set { throw not.valid; }
		}


		public List<variable> variables;

		BitArray used_corefs = new BitArray(256);

		public int GetFreeCorefNum()
		{
			int n = used_corefs.OfType<bool>().IndexOfFirst(used => !used);
			used_corefs[n] = true;
			return n + 1;
		}

		public void FreeCorefNum(int n)
		{
			n--;
			if (!used_corefs[n])
				throw new Exception();
			used_corefs[n] = false;
		}

		public override IEnumerable<IRenderPairs> FeatValuePairs
		{
			get
			{
				yield return new FeatureValueNode("HOOK", hook, true);

				IRenderNode rn = new ListValueNode(rels.Cast<IRenderNode>());
				yield return new FeatureValueNode("RELS", rn, true);

				rn = new ListValueNode(hcons.Cast<IRenderNode>());
				yield return new FeatureValueNode("HCONS", rn, true);
			}
		}

		public override IEnumerable<av_feat> fvp()
		{
			yield return new av_feat("HOOK", hook);
			yield return new av_feat("RELS", rels);
			yield return new av_feat("HCONS", hcons);
		}

		public void EquateQeqScopes()
		{
			foreach (var qeq in hcons)
			{
				qeq.Harg.JoinWith(qeq.Larg);
			}
		}

		public void SwapRelPositions(int i, int j)
		{
			if (i >= rels.Count || j >= rels.Count)
				throw new Exception();
			var tmp = rels[i];
			rels[i] = rels[j];
			rels[j] = tmp;

			int id = rels[i].id;
			rels[i].id = rels[j].id;
			rels[j].id = id;

			//RenumberVars();
		}

		public void RenumberVars()
		{
			next_tag = 1;
			var arr = variables.Where(v => v != null).ToArray();
			variables.Clear();
			foreach (var v in arr.Select(vv => new { minpar = vv.parents.ArgMin(p => p.id), vv })
								.OrderBy(a => a.minpar.id)
								.ThenBy(a => a.minpar.rgav.Values.IndexOfFirst(x => x == a.vv))
								.Select(a => a.vv))
			{
				v.tag = next_tag++;
				variables.Add(v);
			}
		}

		public void DeleteFeature(String s_feat)
		{
			hook.DeleteFeature(s_feat);

			foreach (var r in rels)
				r.DeleteFeature(s_feat);

			foreach (var qeq in hcons)
				qeq.DeleteFeature(s_feat);
		}


		public void Walk(Restrictor r, Action<arr_tfs_entry> callback)
		{
			new _tfs_walk_helper(this, r,  callback);
		}

		public HashSet<atomic_base> seen;
		public String TextDisplay()
		{
			seen = new HashSet<atomic_base>();
			StringBuilder sb = new StringBuilder();

			//sb.AppendFormatLine("{0}{1} {2}{3}", ltop.StringTag, FormatEdge(ts_ltop), index.StringTag, FormatEdge(ts_index));
			sb.AppendFormatLine("{0}{1} {2}{3}", hook.Ltop.StringTag, FormatEdge(hook.Ltop), hook.Index.StringTag, FormatEdge(hook.Index));

			if (rels != null)
			{
				sb.Append("{ ");
				for (int i = 0; i < rels.Count; i++)
				{
					if (i > 0)
						sb.Append(Environment.NewLine + "  ");
					sb.Append(rels[i].Text);
				}
				sb.AppendLine(" }");
			}
			if (hcons != null)
			{
				sb.Append("{ ");
				for (int i = 0; i < hcons.Count; i++)
				{
					if (i > 0)
						sb.Append(",  ");
					sb.Append(hcons[i].Text);
				}
				sb.AppendLine(" }");
			}
			return sb.ToString();
		}

		public string FormatEdge(atomic_base node)
		{
			if (!seen.Add(node))
				return "";
			String s = node.fvp()
							.Select(av => String.Format("{0} {1}", av.s_feat.ToUpper(), av.value.Label) + FormatEdge(av.value))
							.StringJoin(" ");
			if (s != "")
				s = "{ " + s + " }";
			return s;
		}

	};
#endif
}